/*
 * drivers/video/sunxi/disp2/disp/de/lowlevel_v2x/de_lti.c
 *
 * Copyright (c) 2007-2019 Allwinnertech Co., Ltd.
 * Author: zhengxiaobin <zhengxiaobin@allwinnertech.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */
#include "de_feat.h"

#if defined(CONFIG_DISP2_SUNXI_SUPPORT_SMBL)

#include "de_lti_type.h"
#include "de_enhance.h"

static volatile struct __lti_reg_t *lti_dev[DE_NUM][CHN_NUM];
static struct de_reg_blocks lti_block[DE_NUM][CHN_NUM];

/*******************************************************************************
 * function       : de_lti_set_reg_base(unsigned int sel, unsigned int chno,
 *                    unsigned int base)
 * description    : set lti reg base
 * parameters     :
 *                  sel         <rtmx select>
 *                  chno        <overlay select>
 *                  base        <reg base>
 * return         :
 *                  success
 ******************************************************************************/
int de_lti_set_reg_base(unsigned int sel, unsigned int chno, void *base)
{
	lti_dev[sel][chno] = (struct __lti_reg_t *) base;

	return 0;
}

int de_lti_update_regs(unsigned int sel, unsigned int chno)
{
	if (lti_block[sel][chno].dirty == 0x1) {
		regwrite((void *)lti_block[sel][chno].off,
		       lti_block[sel][chno].val, lti_block[sel][chno].size);
		lti_block[sel][chno].dirty = 0x0;
	}

	return 0;
}

int de_lti_init(unsigned int sel, unsigned int chno, uintptr_t reg_base)
{
	uintptr_t base;
	void *memory;

	/* FIXME  display path offset should be defined */
	base = reg_base + (sel + 1) * 0x00100000 + LTI_OFST;
#if defined(CONFIG_INDEPENDENT_DE)
	if (sel)
		base = base - 0x00100000;
#endif
	__inf("sel %d, lti_base[%d]=0x%p\n", sel, chno, (void *)base);

	memory = kmalloc(sizeof(struct __lti_reg_t), GFP_KERNEL | __GFP_ZERO);
	if (NULL == memory) {
		__wrn("malloc lti[%d][%d] memory fail! size=0x%x\n", sel, chno,
		      (unsigned int)sizeof(struct __lti_reg_t));
		return -1;
	}

	lti_block[sel][chno].off = base;
	lti_block[sel][chno].val = memory;
	lti_block[sel][chno].size = 0x40;
	lti_block[sel][chno].dirty = 0;

	de_lti_set_reg_base(sel, chno, memory);

	return 0;
}

/*******************************************************************************
 * function       : de_lti_enable(unsigned int sel, unsigned int chno,
 *                  unsigned int en)
 * description    : enable/disable lti
 * parameters     :
 *                  sel         <rtmx select>
 *                  chno        <overlay select>
 *                  en          <enable: 0-diable; 1-enable>
 * return         :
 *                  success
 ******************************************************************************/
int de_lti_enable(unsigned int sel, unsigned int chno, unsigned int en)
{
	lti_dev[sel][chno]->ctrl.bits.en = en;
	lti_block[sel][chno].dirty = 1;
	return 0;
}

/*******************************************************************************
 * function       : de_lti_set_size(unsigned int sel, unsigned int chno,
 *                    unsigned int width, unsigned int height)
 * description    : set lti size
 * parameters     :
 *                  sel         <rtmx select>
 *                  chno        <overlay select>
 *                  width       <input width>
 *                                      height  <input height>
 * return         :
 *                  success
******************************************************************************/
int de_lti_set_size(unsigned int sel, unsigned int chno, unsigned int width,
		    unsigned int height)
{
	lti_dev[sel][chno]->size.bits.width = width - 1;
	lti_dev[sel][chno]->size.bits.height = height - 1;
	lti_block[sel][chno].dirty = 1;
	return 0;
}

/*******************************************************************************
 * function       : de_lti_set_window(unsigned int sel, unsigned int chno,
 *                   unsigned int win_enable, struct de_rect window)
 * description    : set lti window
 * parameters     :
 *                  sel         <rtmx select>
 *                  chno        <overlay select>
 *                  win_enable  <enable: 0-window mode diable;
 *                                       1-window mode enable>
 *                  window  <window rectangle>
 * return         :
 *                  success
 ******************************************************************************/
int de_lti_set_window(unsigned int sel, unsigned int chno,
		      unsigned int win_enable, struct de_rect window)
{
	lti_dev[sel][chno]->ctrl.bits.win_en = win_enable;

	if (win_enable) {
		lti_dev[sel][chno]->win0.bits.win_left = window.x;
		lti_dev[sel][chno]->win0.bits.win_top = window.y;
		lti_dev[sel][chno]->win1.bits.win_right =
		    window.x + window.w - 1;
		lti_dev[sel][chno]->win1.bits.win_bot = window.y + window.h - 1;
	}
	lti_block[sel][chno].dirty = 1;
	return 0;
}

/*******************************************************************************
 * function       : de_lti_set_para(unsigned int sel, unsigned int chno,
 *                    unsigned int gain)
 * description    : set lti para
 * parameters     :
 *                  sel         <rtmx select>
 *                  chno        <overlay select>
 *                  gain        <lti gain: normal setting 0-3>
 * return         :
 *                  success
 ******************************************************************************/
int de_lti_set_para(unsigned int sel, unsigned int chno, unsigned int gain)
{
	lti_dev[sel][chno]->gain.bits.lti_fil_gain = gain;

	lti_dev[sel][chno]->ctrl.bits.sel = 0;
	lti_dev[sel][chno]->ctrl.bits.nonl_en = 0;

	lti_dev[sel][chno]->coef0.bits.c0 = 127;
	lti_dev[sel][chno]->coef0.bits.c1 = 64;
	lti_dev[sel][chno]->coef1.bits.c2 = 0xe0;
	lti_dev[sel][chno]->coef1.bits.c3 = 0xc0;
	lti_dev[sel][chno]->coef2.bits.c4 = 0xe0;

	lti_dev[sel][chno]->corth.bits.lti_cor_th = 4;
	lti_dev[sel][chno]->diff.bits.offset = 32;
	lti_dev[sel][chno]->diff.bits.slope = 4;
	lti_dev[sel][chno]->edge_gain.bits.edge_gain = 1;
	lti_dev[sel][chno]->os_con.bits.core_x = 0;
	lti_dev[sel][chno]->os_con.bits.clip = 40;
	lti_dev[sel][chno]->os_con.bits.peak_limit = 1;

	lti_dev[sel][chno]->win_range.bits.win_range = 2;
	lti_dev[sel][chno]->elvel_th.bits.elvel_th = 32;

	lti_block[sel][chno].dirty = 1;
	return 0;
}

/*******************************************************************************
 * function       : de_lti_info2para(unsigned int sharp, struct de_rect window,
 *                   struct __lti_config_data *para)
 * description    : info->para conversion
 * parameters     :
 *                  sharp               <gain info from user>
 *                  window              <window info>
 *                  para                <bsp para>
 * return         :
 *                  success
 ******************************************************************************/
int de_lti_info2para(unsigned int sharp, struct de_rect window,
		     struct __lti_config_data *para)
{
	int mode;
	int lti_para[LTI_PARA_NUM][LTI_MODE_NUM] = {
		{0, 3},		/* gain */
	};

	/* parameters */
	mode = sharp & 0xf;
	para->lti_en = mode ? 1 : 0;
	para->gain = lti_para[0][mode];

	return 0;
}
#endif
